# -*- coding: UTF-8 -*-
# 扫码二维码登录相关事宜

from .BaseService import BaseService

class WechatUserLoginInService(BaseService):
    
    def getQRuuid(self):
        url = '%s/jslogin' % self.core.config.WX_BASE_URL
        params = {'appid':self.core.config.PARAM_APPID, 'fun':'new'}
        request = self.core.loader.lib('request', transport_core = True)
        mSession = self.core.loader.newInstance('requests', 'Session')
        responseRes = request.sGet(mSession = mSession, url = url, params = params, headers = self.core.config.DEFAULT_HEADER)
        if responseRes.success:
            matches = self.core.loader.module('re').findall(r'= (\S+?);', responseRes.data.text)
            return matches[1].strip('"') if matches and len(matches) == 2 and int(matches[0]) == 200 else ''
        else:
            return ''


    def getQRcode(self, uuid, cmdQrcode = False):
        qrcodeUrl = '%s/l/%s' % (self.core.config.WX_BASE_URL, uuid)
        qrCode = self.core.loader.module('pyqrcode').create(qrcodeUrl)
        savepath = self.core.config.QRCODE_SAVE_DIR + uuid + self.core.config.QRCODE_SAVE_EXT
        qrCode.png(savepath, scale=20)

        if cmdQrcode:
            self.core.lib.stdout(qrCode.terminal(quiet_zone=1), isExit = False)

        return self.core.lib.outputobj(True, '成功获取二维码路径', savepath)


    def isNeedScanQrcode(self):
        mSession = self.core.loader.instance('requests', 'Session')
        if not isinstance(mSession, object) or not hasattr(mSession, 'cookies') or not hasattr(mSession.cookies, 'get_dict'):
            return self.core.lib.outputobj(False, '用户登录cookie获取失败')

        cookiesDict = mSession.cookies.get_dict()
        if not cookiesDict.has_key('wxuin'):
            return self.core.lib.outputobj(False, '非法的cookie')

        url = '%s/cgi-bin/mmwebwx-bin/webwxpushloginurl' % self.core.config.WX_BASE_URL
        request = self.core.loader.lib('request', transport_core = True)
        reponseRes = request.sGet(url = url, params = {'uin' : cookiesDict['wxuin']}, headers = self.core.config.DEFAULT_HEADER)
        if not reponseRes.success:
            return self.core.lib.outputobj(False, '免扫码登录请求异常')

        try:
            content = reponseRes.data.json()
            if content.has_key('uuid') and int(content.get('ret', -1)) == 0:
                return self.core.lib.outputobj(True, '免扫码登录请求成功', content['uuid'])
            else:
                return self.core.lib.outputobj(False, 'cookie已失效', content)
        except Exception as e:
            return self.core.lib.outputobj(False, '免扫码登录异常：%s' % str(e))


    def saveLoginState(self):
        if not hasattr(self.core.config, 'LOGIN_INFO_PATH'):
            return self.core.lib.outputobj(False, '用户登录信息存储配置项不存在')

        mSession = self.core.loader.getInstance('requests', 'Session')
        if mSession is None:
            return self.core.lib.outputobj(False, '用户session获取失败')

        with open(self.core.config.LOGIN_INFO_PATH, 'wb') as f:
            self.core.loader.module('pickle').dump(mSession.cookies.get_dict(), f)

        return self.core.lib.outputobj(True, '用户登录信息保存成功')


    def loadLoginState(self):
        if not hasattr(self.core.config, 'LOGIN_INFO_PATH'):
            return self.core.lib.outputobj(False, '用户登录信息存储配置项不存在')

        mPath = self.core.loader.module('os.path')
        if not mPath.exists(self.core.config.LOGIN_INFO_PATH):
            return self.core.lib.outputobj(False, '用户登录信息存储配置文件不存在')

        with open(self.core.config.LOGIN_INFO_PATH, 'rb') as f:
            mPickle = self.core.loader.module('pickle')
            mSession = self.core.loader.newInstance('requests', 'Session')
            mSession.cookies = self.core.loader.module('requests.utils').cookiejar_from_dict(mPickle.load(f))
        return self.core.lib.outputobj(True, '成功获取用户登录状态')


    def prepareLoginInfo(self, uuid):
        mRe = self.core.loader.module('re')
        mTime = self.core.loader.module('time')
        retryNum = 1
        operateMsg = []
        while retryNum < 10:
            scanQrcodeInfo = self._getScanQrcodeInfo(uuid)
            matches = mRe.search(r'window.code=(\d+)', scanQrcodeInfo)
            if not matches:
                operateMsg.append('尝试次数[%d]错误信息：%s' % (retryNum, '未匹配到相应的状态码'))
                retryNum += 1
                mTime.sleep(3)
                continue
            '''
            ... -1  未知错误
            ... 200 扫码成功
            ... 201 等待扫码
            ... 400 无效的二维码
            ... 408 二维码过期等
            '''
            state = int(matches.group(1))
            if state == 200:
                fetchRes = self._initUserLoginInfo(mRe.search(r'window.redirect_uri="(\S+)";', scanQrcodeInfo).group(1))
                if fetchRes.success:
                    operateMsg.append('尝试次数[%d]预登录成功：%s' % (retryNum, fetchRes.message))
                    return self.core.lib.outputobj(True, '|'.join(operateMsg), fetchRes.data)
                operateMsg.append('尝试次数[%d]错误信息：[%d]%s' % (retryNum, fetchRes.errcode, fetchRes.message))
                if fetchRes.errcode == 1203:
                    return self.core.lib.outputobj(False, '|'.join(operateMsg))
            elif state == 201:
                operateMsg.append('尝试次数[%d]错误信息：微信客户端未许可' % retryNum)
            else:
                operateMsg.append('尝试次数[%d]错误信息：二维码失效，错误码[%d]' % (retryNum, state))
                break
            retryNum += 1
            mTime.sleep(3)
        return self.core.lib.outputobj(False, '|'.join(operateMsg))


    def _getScanQrcodeInfo(self, uuid):
        url = '%s/cgi-bin/mmwebwx-bin/login' % self.core.config.WX_BASE_URL
        currentTime = int(self.core.loader.module('time').time())
        params = {
            'loginicon' : 'true',
            'uuid' : uuid,
            'tip' : 0,
            'r' : int(-currentTime / 1579),
            '_' : currentTime
        }
        request = self.core.loader.lib('request', transport_core = True)
        responseRes = request.sGet(url = url, params = params, headers = self.core.config.DEFAULT_HEADER)
        return responseRes.data.text if responseRes.success else ''


    def _initUserLoginInfo(self, loginRedirectUrl):
        fetchBaseParamsRes = self._fetchLoginBaseRequestParams(loginRedirectUrl)
        if not fetchBaseParamsRes.success:
            return fetchBaseParamsRes

        mUserInfoDetail = self.core.loader.instance('do', 'UserinfoDetail')
        userinfoDetailDO = mUserInfoDetail.newDO()
        userinfoDetailDO.login_url = loginRedirectUrl[:loginRedirectUrl.rfind('/')]
        userinfoDetailDO.file_url, userinfoDetailDO.sync_check_url = self._fetchPageSyncUrlAndFileUrl(userinfoDetailDO.login_url)
        userinfoDetailDO.device_id = 'e' + repr(self.core.loader.module('random').random())[2:17]
        userinfoDetailDO.login_time = int(self.core.loader.module('time').time() * 1e3)
        userinfoDetailDO.uin = fetchBaseParamsRes.data['wxuin']
        userinfoDetailDO.sid = fetchBaseParamsRes.data['wxsid']
        userinfoDetailDO.skey = fetchBaseParamsRes.data['skey']
        userinfoDetailDO.pass_ticket = fetchBaseParamsRes.data['pass_ticket']
        userinfoDetailDO.base_request_params = {
            'Skey' : userinfoDetailDO.skey,
            'Sid' : userinfoDetailDO.sid,
            'DeviceID' : userinfoDetailDO.pass_ticket,
            'Uin' : userinfoDetailDO.uin
        }
        userinfoDetailDO.session = self.core.loader.getInstance('requests', 'Session')

        mUserInfoDetail.save(userinfoDetailDO.uin, userinfoDetailDO)
        return self.core.lib.outputobj(True, '成功获取用户扫码登录信息', userinfoDetailDO.uin)


    def _fetchLoginBaseRequestParams(self, loginRedirectUrl):
        request = self.core.loader.lib('request', transport_core = True)
        responseRes = request.sGet(url = loginRedirectUrl, headers = self.core.config.DEFAULT_HEADER, allow_redirects = False)
        if not responseRes.success:
            return self.core.lib.outputobj(False, responseRes.message)

        errcode = 0
        paramDict = {}
        errmsg = '参数获取失败，可能是登录跳转链接失效'
        for node in self.core.loader.module('xml.dom.minidom').parseString(responseRes.data.text).documentElement.childNodes:
            if not node.childNodes:
                continue
            if node.nodeName in ('skey', 'wxsid', 'wxuin', 'pass_ticket'):
                paramDict[node.nodeName] = node.childNodes[0].data
            elif node.nodeName == 'ret':
                errcode = int(node.childNodes[0].data)
            elif node.nodeName == 'message':
                errmsg = node.childNodes[0].data

        return self.core.lib.outputobj(True, '成功获取参数', paramDict) if len(paramDict) == 4 else self.core.lib.outputobj(False, errmsg, None, errcode)


    def _fetchPageSyncUrlAndFileUrl(self, loginUrl):
        for targetUrl, combineUrl in self.core.config.WX_LOGIN_URL_LIST:
            if targetUrl not in loginUrl:
                continue
            return ['https://%s/cgi-bin/mmwebwx-bin' % url for url in combineUrl]

        return [loginUrl, loginUrl]
